"""
Update all Document.web_uris.

Revision ID: 9f5e274b202c
Revises: e10ce4472966
Create Date: 2017-01-20 16:07:03.442975
"""

import logging
from collections import namedtuple
from urllib.parse import urlparse

import sqlalchemy as sa
from alembic import op
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, subqueryload

revision = "9f5e274b202c"
down_revision = "e10ce4472966"


Base = declarative_base()
Session = sessionmaker()
log = logging.getLogger(__name__)


class Window(namedtuple("Window", ["start", "end"])):  # noqa: PYI024, SLOT002
    pass


class Document(Base):
    __tablename__ = "document"
    id = sa.Column(sa.Integer, primary_key=True)
    updated = sa.Column(sa.DateTime)
    web_uri = sa.Column(sa.UnicodeText())
    document_uris = sa.orm.relationship(
        "DocumentURI", backref="document", order_by="DocumentURI.updated.desc()"
    )

    def updated_web_uri(self):
        def first_http_url(type_=None):
            for document_uri in self.document_uris:
                uri = document_uri.uri
                if type_ is not None and document_uri.type != type_:
                    continue
                if urlparse(uri).scheme not in ["http", "https"]:
                    continue
                return document_uri.uri

            return None

        return (
            first_http_url(type_="self-claim")
            or first_http_url(type_="rel-canonical")
            or first_http_url()
        )


class DocumentURI(Base):
    __tablename__ = "document_uri"
    id = sa.Column(sa.Integer, primary_key=True)
    updated = sa.Column(sa.DateTime)
    uri = sa.Column(sa.UnicodeText)
    type = sa.Column(sa.UnicodeText, nullable=False, default="", server_default="")
    document_id = sa.Column(sa.Integer, sa.ForeignKey("document.id"), nullable=False)


def upgrade():
    session = Session(bind=op.get_bind())

    windows = _fetch_windows(session)
    session.rollback()

    updated = 0
    not_changed = 0
    for window in windows:
        query = (
            session.query(Document)
            .filter(Document.updated.between(window.start, window.end))
            .options(subqueryload(Document.document_uris))
            .order_by(Document.updated.asc())
        )

        for doc in query:
            updated_web_uri = doc.updated_web_uri()
            if updated_web_uri and updated_web_uri != doc.web_uri:
                doc.web_uri = updated_web_uri
                updated += 1
            else:
                not_changed += 1

        session.commit()

    log.info(f"Updated {updated} web_uris")  # noqa: G004
    log.info(f"Left {not_changed} web_uris unchanged")  # noqa: G004


def downgrade():
    pass


def _fetch_windows(session, chunksize=100):
    updated = (
        session.query(Document.updated)
        .execution_options(stream_results=True)
        .order_by(Document.updated.desc())
        .all()
    )

    count = len(updated)
    windows = [
        Window(
            start=updated[min(x + chunksize, count) - 1].updated, end=updated[x].updated
        )
        for x in range(0, count, chunksize)
    ]

    return windows
